package com.ma.imclient.sender;

import cn.hutool.core.collection.CollUtil;
import com.ma.common.contant.IMRedisKey;
import com.ma.common.enums.IMCmdType;
import com.ma.common.enums.IMListenerType;
import com.ma.common.enums.IMSendCode;
import com.ma.common.enums.IMTerminalType;
import com.ma.common.model.*;
import com.ma.common.mq.RedisMQTemplate;
import com.ma.imclient.listener.MessageListenerMulticaster;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;
@RequiredArgsConstructor
@Component
public class ImSender {
    @Autowired
    private RedisMQTemplate redisMQTemplate;

    @Value("${spring.application.name}")
    private String appName;

    private final MessageListenerMulticaster listenerMulticaster;

    public Boolean isOnline(Long userId) {
        String key = String.join(":",IMRedisKey.IM_USER_SERVER_ID,userId.toString(),"*");
        return !Objects.requireNonNull(redisMQTemplate.keys(key)).isEmpty();
    }

    public List<Long> getOnlineUser(List<Long> userIds) {
        return new LinkedList<>(getOnlineTerminal(userIds).keySet());

    }

    public Map<Long, List<IMTerminalType>> getOnlineTerminal(List<Long> userIds) {
        if(CollUtil.isEmpty(userIds)){
            return Collections.emptyMap();
        }
        //把所有用户的key都存起来
        HashMap<String, IMUserInfo> userMap = new HashMap<>();
        for (Long id : userIds) {
            for (Integer terminal : IMTerminalType.codes()) {
                String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, id.toString(), terminal.toString());
                userMap.put(key,new IMUserInfo(id,terminal));
            }
        }
        //批量拉取
        List<Object> serverids = redisMQTemplate.opsForValue().multiGet(userMap.keySet());
        int idx =0;
        HashMap<Long, List<IMTerminalType>> onlineMap = new HashMap<>();
        for (Map.Entry<String, IMUserInfo> entry : userMap.entrySet()) {
            //serverId有值表示用户在线
            if (serverids.get(idx++)!=null){
                IMUserInfo userInfo = entry.getValue();
                List<IMTerminalType> terminals = onlineMap.computeIfAbsent(userInfo.getId(), o -> new LinkedList<>());
                terminals.add(IMTerminalType.fromCode(userInfo.getTerminal()));
            }
        }
        //去重并返回
        return onlineMap;
    }

    public <T> void sendSystemMessage(IMSystemMessae<T> messae) {
        //根据群聊每个成员所连的IM-Server进行分组
        HashMap<String, IMUserInfo> sendMap = new HashMap<>();
        for (Integer terminal : messae.getRecvTerminals()) {
            messae.getRecvIds().forEach(id ->{
                String key = String.join(":",IMRedisKey.IM_USER_SERVER_ID,id.toString(),terminal.toString());
                sendMap.put(key,new IMUserInfo(id,terminal));
            });
        }
        //批量拉取
        List<Object> serverIds = redisMQTemplate.opsForValue().multiGet(sendMap.keySet());
        //格式 map<serverId,list<recvId>>
        HashMap<Integer, List<IMUserInfo>> serverMap = new HashMap<>();
        LinkedList<IMUserInfo> offlineUsers = new LinkedList<>();
        int idx =0 ;
        for (Map.Entry<String, IMUserInfo> entry : sendMap.entrySet()) {
            Integer serverId = (Integer) serverIds.get(idx++);
            if (!Objects.isNull(serverId)){
                List<IMUserInfo> list = serverMap.computeIfAbsent(serverId, o -> new LinkedList<>());
                list.add(entry.getValue());
            }else {
                //加入离线列表
                offlineUsers.add(entry.getValue());
            }
        }
        //逐个server发送
        for (Map.Entry<Integer, List<IMUserInfo>> entry : serverMap.entrySet()) {
            IMRecvInfo recvInfo = new IMRecvInfo();
            recvInfo.setCmd(IMCmdType.SYSTEM_MESSAGE.code());
            recvInfo.setReceivers(new LinkedList<>(entry.getValue()));
            recvInfo.setServiceName(appName);
            recvInfo.setSendResult(messae.getSendResult());
            recvInfo.setData(messae.getData());
            //推送至队列
            String key = String.join(":",IMRedisKey.IM_MESSAGE_SYSTEM_QUEUE,entry.getKey().toString());
            redisMQTemplate.opsForList().rightPush(key,recvInfo);
        }
        //对离线用户回复消息状态
        if (messae.getSendResult()&& !offlineUsers.isEmpty()){
            LinkedList<IMSendResult> results = new LinkedList<>();
            for (IMUserInfo offlineUser : offlineUsers) {
                IMSendResult sendResult = new IMSendResult();
                sendResult.setReceiver(offlineUser);
                sendResult.setCode(IMSendCode.NOT_ONLINE.code());
                sendResult.setData(messae.getData());
                results.add(sendResult);
            }
            listenerMulticaster.multicast(IMListenerType.SYSTEM_MESSAGE,results);
        }
    }

    public <T> void sendGroupMessage(IMGroupMessage<T> message) {
        //根据群聊每个成员所连的IM-Server，进行分组
        HashMap<String, IMUserInfo> sendMap = new HashMap<>();
        for (Integer terminal : message.getRecvTerminal()) {
            message.getRecvIds().forEach(id->{
                String key = String.join(":",IMRedisKey.IM_USER_SERVER_ID,id.toString(),terminal.toString());
                sendMap.put(key,new IMUserInfo(id,terminal));
            });
        }
        //批量拉取
        List<Object> serverIds = redisMQTemplate.opsForValue().multiGet(sendMap.keySet());
        //格式 map<服务器id，list<接收方>>
        HashMap<Integer, List<IMUserInfo>> serverMap = new HashMap<>();
        LinkedList<IMUserInfo> offlineUsers = new LinkedList<>();
        int idx =0;
        for (Map.Entry<String, IMUserInfo> entry : sendMap.entrySet()) {
            Integer serverId = (Integer) serverIds.get(idx++);
            if (!Objects.isNull(serverId)){
                List<IMUserInfo> list = serverMap.computeIfAbsent(serverId, o -> new LinkedList<>());
                list.add(entry.getValue());
            }else {
                //加入离线列表
                offlineUsers.add(entry.getValue());
            }
        }
        //逐个server发送
        for (Map.Entry<Integer, List<IMUserInfo>> entry : serverMap.entrySet()) {
            IMRecvInfo recvInfo = new IMRecvInfo();
            recvInfo.setCmd(IMCmdType.GROUP_MESSAGE.code());
            recvInfo.setReceivers(new LinkedList<>(entry.getValue()));
            recvInfo.setSender(message.getSender());
            recvInfo.setServiceName(appName);
            recvInfo.setSendResult(message.getSendResult());
            recvInfo.setData(message.getData());
            //推送至队列
            String key = String.join(":",IMRedisKey.IM_MESSAGE_GROUP_QUEUE,entry.getKey().toString());
            redisMQTemplate.opsForList().rightPush(key,recvInfo);
        }
        //对离线用户回复消息状态
        if (message.getSendResult() && !offlineUsers.isEmpty()){
            LinkedList<IMSendResult> results = new LinkedList<>();
            for (IMUserInfo offlineUser : offlineUsers) {
                IMSendResult result = new IMSendResult();
                result.setReceiver(offlineUser);
                result.setCode(IMSendCode.NOT_ONLINE.code());
                result.setData(message.getData());
                results.add(result);
            }
            listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE, results);
        }
    }

    public <T> void sendPrivateMessage(IMPrivateMessage<T> messae) {
        LinkedList<IMSendResult> results = new LinkedList<>();
        if (!Objects.isNull(messae.getRecvId())){
            for (Integer terminal : messae.getRecvTerminals()) {
                //获取对方连接的channelId
                String key = String.join(":",IMRedisKey.IM_USER_SERVER_ID,messae.getRecvId().toString(),terminal.toString());
                Integer serverId = (Integer) redisMQTemplate.opsForValue().get(key);
                //如果对方在线，将数据存储在redis,等待拉取推送
                if (serverId != null){
                    String sendKey = String.join(":",IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE,serverId.toString());
                    IMRecvInfo recvInfo = new IMRecvInfo();
                    recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code());
                    recvInfo.setSendResult(messae.getSendResult());
                    recvInfo.setServiceName(appName);
                    recvInfo.setSender(messae.getSend());
                    recvInfo.setData(messae.getData());
                    redisMQTemplate.opsForList().rightPush(sendKey,recvInfo);
                }else {
                    IMSendResult result = new IMSendResult();
                    result.setSender(messae.getSend());
                    result.setReceiver(new IMUserInfo(messae.getRecvId(),terminal));
                    result.setData(messae.getData());
                    results.add(result);
                }
            }
        }
        //推送给自己的其他终端
        if (messae.getSendToSelf()){
            for (Integer terminal : IMTerminalType.codes()) {
                if (messae.getSend().getTerminal().equals(terminal)){
                    continue;
                }
                //获取终端连接的channelid
                String key = String.join(":",IMRedisKey.IM_USER_SERVER_ID,messae.getSend().getId().toString(),terminal.toString());
                Integer serverId = (Integer) redisMQTemplate.opsForValue().get(key);
                //如果终端在线，将数据存储至redis，等待拉取推送
                String sendKey = String.join(":",IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE,serverId.toString());
                IMRecvInfo recvInfo = new IMRecvInfo();
                //自己的小修不需要回退消息结果
                recvInfo.setSendResult(false);
                recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code());
                recvInfo.setSender(messae.getSend());
                recvInfo.setData(messae.getData());
                recvInfo.setReceivers(Collections.singletonList(new IMUserInfo(messae.getSend().getId(),terminal)));
                redisMQTemplate.opsForList().rightPush(sendKey,recvInfo);
            }
        }
        //对离线用户回复消息状态
        if (messae.getSendResult()&& !results.isEmpty()){
            listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE,results);
        }
    }

    public Boolean isOnline(Long userId, IMTerminalType terminalType) {
        String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID,userId.toString(),terminalType.code().toString());
        return redisMQTemplate.hasKey(key);
    }
}
